/////////////////////////////////////////////////////////////////////////////
//
// XepdtClient.cpp
//
//
/////////////////////////////////////////////////////////////////////////////

#include "StdAfx.h"
#include <process.h>
#include <ctime>
#include "XepdtClient.h"
#include "NetCardInfo.h"
#include "CodeLib.h"

using namespace gloox;
/////////////////////////////////////////////////////////////////////////////
// CXepdtClient

/////////////////////////////////////////////////////////////////////////////
// CXepdtClient constructor and destructor
#pragma warning( disable:4355 )

CXepdtClient::CXepdtClient(const std::string& server)
:Client(server)
,recvThread(this, &CXepdtClient::XepdtRecvProc)
{
	RECEIVE_THREAD_TERMINATED = false;
	m_XepdtConsole.Init();
	m_XepdtConsole.OutputXepdtInfo( "Xepdt Initialize successful !" , CXepdtLogConsole::TEXT_WHITE );

	m_pXepdtDiscoHandler		= NULL;
	m_pXepdtLogHandler			= NULL;
	m_pXepdtConnectListener		= NULL;
	m_pXepdtRosterListener		= NULL;
	m_pXepdtSessionHandler      = NULL;
	m_pXepdtRegistration		= NULL;
}

CXepdtClient::CXepdtClient(const std::string& jid, const std::string& password, int port)
:Client(JID(jid), password, port)
,recvThread(this, &CXepdtClient::XepdtRecvProc)
{
	RECEIVE_THREAD_TERMINATED = false;
	m_XepdtConsole.Init();
	m_XepdtConsole.OutputXepdtInfo( "Xepdt Initialize successful !" , CXepdtLogConsole::TEXT_WHITE );

	m_pXepdtDiscoHandler		= NULL;
	m_pXepdtLogHandler			= NULL;
	m_pXepdtConnectListener		= NULL;
	m_pXepdtRosterListener		= NULL;
	m_pXepdtSessionHandler      = NULL;
	m_pXepdtRegistration		= NULL;
	m_pXepdtFT					= NULL;
}

CXepdtClient::~CXepdtClient()
{
	if(!RECEIVE_THREAD_TERMINATED)
	{
		RECEIVE_THREAD_TERMINATED = true;
		WaitForSingleObject(recvThread.handle_, INFINITE);
	}
	ReleaseRes();
}

/////////////////////////////////////////////////////////////////////////////
// Method implementation

bool CXepdtClient::Connect( std::string strServer, std::string strID, std::string strPwd )
{
	OutputLogInfo( "Xepdt application initialize successful!", CXepdtLogConsole::TEXT_WHITE );

	ASSERT( ! strServer.empty()  );
	ASSERT( ! strID.empty() );
	ASSERT( ! strPwd.empty() );
	if ( strServer.empty() || strID.empty() || strPwd.empty() )
		return FALSE;

	std::string jabberID = strID + "@" + strServer;
	std::string password = strPwd;

	m_jid = jabberID;
	m_jid.setResource( "Xepdt" );

	m_server = strServer;
	m_password = strPwd;

	m_pXepdtDiscoHandler    = new CXepdtDiscoHandler      ();
	m_pXepdtLogHandler      = new CXepdtLogHandler        ( this );
	m_pXepdtSessionHandler  = new CXepdtSessionHandler    ( this );
	m_pXepdtConnectListener = new CXepdtConnectionListener( this );
	m_pXepdtRosterListener	= new CXepdtRosterListener	  ( this );
	m_pXepdtFT				= new CXepdtFT(this);

	ASSERT( m_pXepdtDiscoHandler    != NULL );
	ASSERT( m_pXepdtLogHandler      != NULL );
	ASSERT( m_pXepdtSessionHandler  != NULL );
	ASSERT( m_pXepdtConnectListener != NULL );
	ASSERT( m_pXepdtRosterListener  != NULL );

	m_pXepdtRosterListener->SetClientHandler(m_fGetClients);
	this->rosterManager()->registerRosterListener(m_pXepdtRosterListener);

	setPresence( Presence::Available, 1, "Idle");
	registerConnectionListener( m_pXepdtConnectListener );

	disco()->registerDiscoHandler( m_pXepdtDiscoHandler );
	disco()->setVersion( "Xepdt", "1.0");
	disco()->setIdentity( "Client", "Xepdt");

	logInstance().registerLogHandler( gloox::LogLevelDebug, gloox::LogAreaAll, m_pXepdtLogHandler );

	bool result = login();
	if(!result &&  gloox::ConnAuthenticationFailed == m_pXepdtConnectListener->getConnectionResult()) 
	{
		m_pXepdtRegistration = new CXepdtRegistration();
		RegistrationResult regResult = m_pXepdtRegistration->registerAccount(strServer, strID, strPwd);
		if(regResult == RegistrationSuccess)
		{
			//login again
			result = login();
		}
	}

	if(!result)
	{
		ReleaseRes();
	}

	return result;
}

bool CXepdtClient::login()
{
	if ( connect( false ) == false )
	{		
		return FALSE;
	}

	recvThread.start();

	OutputLogInfo( "Connect server successful!", CXepdtLogConsole::TEXT_WHITE );

	// check the login result 
	// the timeout value is 10 seconds
	bool result = false;
	int wait = 0;
	while(wait <= 10000)
	{
		result = authed();
		if(result) 
			break;

		Sleep(10);
		wait += 10;
	}
	return result;
}

void CXepdtClient::addContact(const std::string& strJID, const std::string& name, const std::string& msg  )
{
	gloox::JID  jid(strJID);
	gloox::StringList groups;
	groups.push_back("friends");
	rosterManager()->subscribe(jid, name, groups, msg);
}

void CXepdtClient::removeContact(const std::string& strJID)
{
	gloox::JID jid(strJID);
	rosterManager()->remove(jid);
}

void CXepdtClient::requestRoster()
{
	rosterManager()->fill();
}

void CXepdtClient::sendFile(const std::string& to, const std::string& file)
{
}

bool CXepdtClient::Disconnect()
{
	disconnect();

	removeConnectionListener( m_pXepdtConnectListener );

	rosterManager()->removeRosterListener();

	disco()->removeDiscoHandler( m_pXepdtDiscoHandler );
	logInstance().removeLogHandler( m_pXepdtLogHandler );

	return TRUE;
}

VOID CXepdtClient::ReleaseRes()
{
	Disconnect();

	if ( m_pXepdtDiscoHandler != NULL )
	{
		delete m_pXepdtDiscoHandler;
		m_pXepdtDiscoHandler = NULL;
	}

	if ( m_pXepdtLogHandler != NULL )
	{
		delete m_pXepdtLogHandler;
		m_pXepdtLogHandler = NULL;
	}

	if ( m_pXepdtSessionHandler != NULL )
	{
		delete m_pXepdtSessionHandler;
		m_pXepdtSessionHandler = NULL;
	}

	if ( m_pXepdtConnectListener != NULL )
	{
		delete m_pXepdtConnectListener;
		m_pXepdtConnectListener = NULL;
	}
	if(m_pXepdtFT != NULL)
	{
		delete m_pXepdtFT;
		m_pXepdtFT = NULL;
	}

	ASSERT( m_pXepdtDiscoHandler    == NULL );
	ASSERT( m_pXepdtLogHandler      == NULL );
	ASSERT( m_pXepdtSessionHandler  == NULL );
	ASSERT( m_pXepdtConnectListener == NULL );
	ASSERT( m_pXepdtFT == NULL );
}

VOID CXepdtClient::OutputLogInfo( std::string strLogInfo, CXepdtLogConsole::TEXT_COLOR textColor )
{
	m_XepdtConsole.OutputXepdtInfo( strLogInfo, textColor );
}

// Handle the message from server
void *CXepdtClient::XepdtRecvProc(void *)
{
	OutputLogInfo( "Start LISTENER thread !", CXepdtLogConsole::TEXT_RED );

	gloox::ConnectionError ce = gloox::ConnNoError;

	while ( !RECEIVE_THREAD_TERMINATED && ce == gloox::ConnNoError)
	{
		ce = recv(2);
		SOCKS5BytestreamServer* pSock5Server = m_pXepdtFT->getSock5Server();
		if(pSock5Server != NULL)
		{
			pSock5Server->recv(2);
		}
	}

	_endthreadex(0);

	return 0;
}

void CXepdtClient::handleTag( Tag* tag )
{	
	std::string uid = tag->findAttribute("id");
	
	m_tagfilterMutex.lock();
	XepdtTagHandlerList::iterator it;
	for(it = m_tagHandlerList.begin(); it!= m_tagHandlerList.end(); it++)
	{
		if((*it).tag.length() == 0)     //hook all the tag messages
		{
			(*it).th->handleTag(tag);
		}
		if((*it).tag == tag->name())
		{
			if( uid == (*it).uid )
			{
				(*it).th->handleTag(tag);
			}
		}
	}
	m_tagfilterMutex.unlock();
	
	ClientBase::handleTag(tag);
}

void CXepdtClient::registerTagHandler( TagHandler* th, const std::string& tag, const std::string& uid )
{
	m_tagfilterMutex.lock();

	XepdtTagHandlerStruct ths;
    ths.tag = tag;
    ths.uid = uid;
    ths.th = th;
    m_tagHandlerList.push_back( ths );

	m_tagfilterMutex.unlock();
}

void CXepdtClient::unregisterTagHandler(TagHandler* th)
{
	m_tagfilterMutex.lock();
	XepdtTagHandlerList::iterator it;
	for(it = m_tagHandlerList.begin(); it!= m_tagHandlerList.end(); it++)
	{
		if((*it).th == th)
		{
			m_tagHandlerList.erase(it);
			break;
		}
	}
	
	m_tagfilterMutex.unlock();
}

std::string CXepdtClient::getMac()
{
	if(m_mac.length() == 0)
	{
		CNetCardInfo netCard;

		m_mac = netCard.getMacAddress();
	}
	return m_mac;
}

std::string CXepdtClient::getUniqeID()
{
	unsigned int uniqueBaseID = (unsigned int)time( 0 );
    char r[21+1];
    sprintf( r, ":%08x:%08x", uniqueBaseID, rand() );
    std::string ret( r, 18 );

	return getMac() + ret; 
}

void CXepdtClient::SetClientHandler(const ClientHandler& GetClients)
{
	m_fGetClients = GetClients;
	if (m_pXepdtRosterListener)
		m_pXepdtRosterListener->SetClientHandler(m_fGetClients);
}